﻿using System;
using System.Collections.Generic;
using System.Linq;
using CCodeTreeLib;
using SqlOrm.Xml;

namespace SqlOrm.TreeBuilders
{
    public class ServiceImplementationTreeBuilder : CCodeTree
    {
        DataDictionary _ddl;
        bool _messagecontracts = true;
        public ServiceImplementationTreeBuilder(DataDictionary ddl)
        {
            _ddl = ddl;
        }
        public void Build()
        {
            var ns = AddStatement(new CCodeNamespace(_ddl.Settings.ServiceImplOutput.clrnamespace));
            Usings.Add(new CCodeUsing("System"));
            Usings.Add(new CCodeUsing("System.ServiceModel"));
            if (_ddl.Settings.ServiceImplOutput.clrnamespace != _ddl.Settings.ServiceContractOutput.clrnamespace)
                Usings.Add(new CCodeUsing(_ddl.Settings.ServiceContractOutput.clrnamespace));
            else
            {
                if (_ddl.Settings.ServiceImplOutput.clrnamespace != _ddl.Settings.DataContractOutput.clrnamespace)
                    Usings.Add(new CCodeUsing(_ddl.Settings.DataContractOutput.clrnamespace));
                Usings.Add(new CCodeUsing("System.Collections.Generic"));
            }

            //var ns = Tree.NameSpace;
            var basecls = ns.AddClass(new CCodeClass(_ddl.Settings.ServiceImplOutput.ServiceName + "Base"));
            basecls.Modifier = Modifier.Public;
            basecls.IsAbstract = true;
            basecls.Attributes.Add(new CCodeAttribute("ServiceBehavior", string.Format(@"Name = ""{0}"", 
		Namespace = ""{1}"", 
		InstanceContextMode = InstanceContextMode.{2}, 
		ConcurrencyMode = ConcurrencyMode.{3}",_ddl.Settings.ServiceImplOutput.ServiceName, _ddl.Settings.ServiceImplOutput.urinamespace, _ddl.Settings.ServiceImplOutput.InstanceContextMode, _ddl.Settings.ServiceImplOutput.ConcurrencyMode)));
            basecls.ImplementedInterfaces.Add("I" + _ddl.Settings.ServiceContractOutput.ServiceName + "ServiceContract");

            BuildAlls(basecls);
            BuildDeletes(basecls);
            BuildGets(basecls);
            BuildProcs(basecls);
            BuildSearches(basecls);
            BuildUpserts(basecls);

            var derivedcls =ns.AddClass(new CCodeClass(_ddl.Settings.ServiceImplOutput.ServiceName));
            derivedcls.IsPartial = true;
            derivedcls.BaseClass = basecls.Name;
            derivedcls.Modifier = Modifier.Public;

        }

        private void BuildSearches(CCodeClass si)
        {
            foreach (var table in _ddl.Definition.Schema.Table.Where(x => !string.IsNullOrEmpty(x.search)))
            {
                string[] patterns = table.search.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var pattern in patterns)
                {

                    string[] sname = pattern.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    var searchFields = new List<Field>();
                    foreach (var s in sname)
                    {
                        Field f = table.Field.Where(x => x.name == s).FirstOrDefault();
                        if (f != null)
                            searchFields.Add(f);
                    }
                    string procArgsName = string.Empty;
                    Array.ForEach(searchFields.ToArray(), x => procArgsName += x.title);
                    string methodName = string.Format("Search{0}By{1}", table.name, procArgsName);


                    string returnType = string.Empty;
                    if (_messagecontracts)
                        returnType = methodName + "Response";
                    else
                        returnType = "List<" + table.name + ">";

                    var sm = si.AddMember(new CCodeMethod(returnType, methodName));
                    //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, methodName)));
                    sm.IsVirtual = true;

                    if (_messagecontracts)
                    {
                        sm.Parameters.Add(new CCodeField(methodName + "Request", table.name.ToLower() + "Request"));
                    }
                    else
                    {
                        foreach (var s in searchFields)
                        {
                            sm.Parameters.Add(new CCodeField(s.ClrDataType, s.name));
                        }
                    }
                    sm.AddStatement("return null;");
                }
            }
        }

        private void BuildProcs(CCodeClass si)
        {
            if (_ddl.Definition.Procedures!=null)
            {
                foreach (var proc in _ddl.Definition.Procedures)
                {
                    string returnType = string.Empty;
                    if (_messagecontracts)
                        returnType = proc.name + "Response";
                    else
                    {
                        if (proc.Input != null && proc.Input.Count() > 0)
                            returnType = "List<" + proc.name + "Output" + ">";
                        else
                            returnType = "void";
                    }
                    var sm = si.AddMember(new CCodeMethod(returnType, proc.name));
                    //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, proc.name)));
                    sm.IsVirtual = true;
                    if (_messagecontracts)
                    {
                        sm.Parameters.Add(new CCodeField(proc.name + "Request", proc.name.ToLower() + "Request"));
                        sm.AddStatement("return null;");
                    }
                    else
                    {
                        if (proc.Input != null)
                        {
                            foreach (var f in proc.Input)
                                sm.Parameters.Add(new CCodeField(f.ClrDataType, f.name));
                        }
                        if (proc.Output != null && proc.NestedFields != null && proc.NestedFields.Count() > 0)
                            sm.AddStatement("return null;");
                    }
                }
            }
        }

        private void BuildUpserts(CCodeClass si)
        {
            foreach (var table in _ddl.Definition.Schema.Table)
            {
                string returnType = "void";// table.name;
                if (_messagecontracts)
                    returnType = "Upsert" + table.name + "Response";
                var sm = si.AddMember(new CCodeMethod(returnType, "Upsert" + table.name));
                //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, "Upsert" + table.name)));
                sm.IsVirtual = true;
                if (_messagecontracts)
                {
                    sm.Parameters.Add(new CCodeField("Upsert" + table.name + "Request", table.name.ToLower() + "Request"));
                }
                else
                {
                    //foreach (var f in table.Field.Where(x => x.key))
                    //    sm.Parameters.Add(new CCodeField(f.ClrDataType, f.name));
                    sm.Parameters.Add(new CCodeField(table.name, table.name.ToLower()));
                }
                if (_messagecontracts)
                    sm.AddStatement("return null;");
            }
        }

        private void BuildAlls(CCodeClass si)
        {
            foreach (var table in _ddl.Definition.Schema.Table.Where(x=>x.all))
            {
                string returnType = "List<" + table.name + ">";
                if (_messagecontracts)
                    returnType = "All" + table.name + "Response";
                var sm = si.AddMember(new CCodeMethod(returnType, "All" + table.name));
                sm.IsVirtual = true;
                //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, "All" + table.name)));
                if (_messagecontracts)
                {
                    sm.Parameters.Add(new CCodeField("All" + table.name + "Request", table.name.ToLower() + "Request"));
                }
                //else
                //{
                //    foreach (var f in table.Field.Where(x => x.key))
                //        sm.Parameters.Add(new CCodeField(f.ClrDataType, f.name));
                //}
                sm.AddStatement("return null;");
            }
        }

        private void BuildDeletes(CCodeClass si)
        {
            foreach (var table in _ddl.Definition.Schema.Table.Where(x => x.delete))
            {
                string returnType = "void";// table.name;
                if (_messagecontracts)
                    returnType = "Delete" + table.name + "Response";
                var sm = si.AddMember(new CCodeMethod(returnType, "Delete" + table.name));
                //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, "Delete" + table.name)));
                sm.IsVirtual = true;
                if (_messagecontracts)
                {
                    sm.Parameters.Add(new CCodeField("Delete" + table.name + "Request", table.name.ToLower() + "Request"));
                }
                else
                {
                    foreach (var f in table.Field.Where(x => x.key))
                        sm.Parameters.Add(new CCodeField(f.ClrDataType, f.name));
                }
                if (_messagecontracts)
                    sm.AddStatement("return null;");
            }
        }

        private void BuildGets(CCodeClass si)
        {
            foreach (var table in _ddl.Definition.Schema.Table)
            {
                string returnType = table.name;
                if (_messagecontracts)
                    returnType = "Get" + table.name + "Response";
                var sm = si.AddMember(new CCodeMethod(returnType, "Get" + table.name));
                //sm.Attributes.Add(new CCodeAttribute("OperationContract", string.Format("IsTerminating = false, IsInitiating = true, IsOneWay = false, AsyncPattern = false, Action = \"{0}/{1}\", ProtectionLevel = ProtectionLevel.None", _ddl.Settings.ServiceContractOutput.urinamespace, "Get" + table.name)));
                sm.IsVirtual = true;
                if (_messagecontracts)
                {
                    sm.Parameters.Add(new CCodeField("Get" + table.name + "Request", table.name.ToLower() + "Request"));
                }
                else
                {
                    foreach (var f in table.Field.Where(x => x.key))
                        sm.Parameters.Add(new CCodeField(f.ClrDataType, f.name));
                }
                sm.AddStatement("return null;");
            }
        }
    }
}
